package com.linfeng.component.datasource.manual.annotation.aspect;

import com.linfeng.component.datasource.manual.annotation.DSType;
import com.linfeng.component.datasource.manual.config.DataSourceNames;
import com.linfeng.component.datasource.manual.config.DynamicDataSource;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * 多数据源，切面处理类
 */
@Slf4j
@Aspect
@Component
@Order(1)
public class DSTypeAspect {

    /**
     * @annotation匹配指定注解的方法* @within匹配指定注解的类* 注意：这里只拦截所注解的类，如果调用的是父类的方法，那么不会拦截，除非父类方法在子类中被覆盖。
     */
    @Pointcut("@annotation(com.linfeng.component.datasource.manual.annotation.DSType) || @within(com.linfeng.component.datasource.manual.annotation.DSType)")
    public void dataSourcePointCut() {
    }

    @Around("dataSourcePointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        Class<?> dataClass = Class.forName(signature.getDeclaringTypeName());
        DSType dsMethod = method.getAnnotation(DSType.class);
        DSType dsClass = dataClass.getAnnotation(DSType.class);
        if (dsMethod != null) {
            //方法优先，如果方法上存在注解，则优先使用方法上的注解
            DynamicDataSource.setDataSource(dsMethod.value());
            log.info("set datasource is " + dsMethod.value());
        } else if (dsClass != null) {
            //其次类优先，如果类上存在注解，则使用类上的注解
            DynamicDataSource.setDataSource(dsClass.value());
            log.info("set datasource is " + dsClass.value());
        } else {
            //如果都不存在，则使用默认
            DynamicDataSource.setDataSource(DataSourceNames.MASTER);
            log.info("set datasource is " + DataSourceNames.MASTER);
        }
        try {
            return point.proceed();
        } finally {
            DynamicDataSource.clearDataSource();
            log.info("clean datasource");
        }
    }
}
